package com.freecloud.javabasics.design.proxy.mybatis.supper;

import com.freecloud.javabasics.design.proxy.mybatis.annotation.FreeSignature;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 插件代理
 * @Author: maomao
 * @Date: 2021-04-11 17:54
 */
public class FreePlugin implements InvocationHandler {

    private final Object target;

    private final FreeInterceptor interceptor;
    private final Map<Class<?>, Set<Method>> signatureMap;

    public FreePlugin(Object target, FreeInterceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
        this.target = target;
        this.interceptor = interceptor;
        this.signatureMap = signatureMap;
    }

    public static Object wrap(Object target, FreeInterceptor interceptor) {
        //解析获取插件代理的方法
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        Class<?> type = target.getClass();
        return Proxy.newProxyInstance(
                type.getClassLoader(),
                target.getClass().getInterfaces(),
                new FreePlugin(target, interceptor, signatureMap));
        //return target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            Set<Method> methods = signatureMap.get(method.getDeclaringClass());
            if (methods != null && methods.contains(method)) {
                return interceptor.intercept(new FreeInvocation(target, method, args));
            }
            return method.invoke(target, args);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 将要代理的插件实现，缓存到Map中
     * @param interceptor
     * @return
     */
    private static Map<Class<?>, Set<Method>> getSignatureMap(FreeInterceptor interceptor) {

        FreeSignature sig = interceptor.getClass().getAnnotation(FreeSignature.class);
        Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
        //todo 本处写死一个FreeTestHandler
        Class<?> type = FreeTestHandler.class;
        Class<?>[] parameterTypes = {String.class};

        Set<Method> methods = signatureMap.computeIfAbsent(type, k -> new HashSet<>());
        try {
            Method method = type.getMethod(sig.method(), parameterTypes);
            methods.add(method);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("Could not find method on " + type + " named " + sig.method() + ". Cause: " + e, e);
        }
        return signatureMap;
    }
}
